<template>
  <div class="container">
    <!--  v-loading 如果加上 .fullscreen 会全屏转圈圈 -->
    <!--  v-loading 如果加上 .lock 可以在转圈圈的时候锁定滚动条 -->
    <div ref="refAppContainer" class="app-container">
      <el-tree default-expand-all :data="list" :props="treeProps">
        <!-- 注意：如果不使用 template 包裹，那么下面的 el-dropdown 只会有一个组件实例，会被每一个树节点复用，如果加上 template 包裹，就不会有这个问题 -->
        <!-- 以后填充插槽内容的时候，就算是默认插槽，也建议加上 template -->
        <!-- 旧版插槽语法：你们以后在公司可能看见的都是旧版插槽语法，因为公司的项目可能做了很久了 -->
        <!-- slot="插槽名称" 指定填充插槽位置 -->
        <!-- slot-scope="作用域数据变量" 接收作用域插槽传递出来的数据 -->
        <!-- <template #default="{ data }"> -->
        <template slot="default" slot-scope="{ data }">
          <!-- 填充的每个节点的自定义结构，作为默认插槽插入到 el-tree 组件内部，组件内部会对其进行遍历循环渲染 -->
          <!-- el-row 一行共有24份 -->
          <el-row style="width: 100%;">
            <!-- 用 span 属性指向该列占多少份 -->
            <el-col :span="20">{{ data.name }}</el-col>
            <el-col :span="4">
              <!-- 内嵌的行还是24份，只不过范围仅在父盒子内 -->
              <el-row>
                <el-col :span="8">{{ data.managerName }}</el-col>
                <el-col :span="16">
                  <!-- .native 监听的事件最终会绑定在哪个元素上呢？ 组件的根元素 -->
                  <el-dropdown @command="handleCommand($event, data)" @click.native.stop>
                    <!-- 注意：这里不能再写一个 <template #default></template> 和上面那个 #default 插槽有语法冲突了 -->
                    <span slot="default">
                      操作<i class="el-icon-arrow-down el-icon--right" />
                    </span>
                    <el-dropdown-menu slot="dropdown">
                      <el-dropdown-item command="add">添加子部门</el-dropdown-item>
                      <el-dropdown-item command="edit">编辑部门</el-dropdown-item>
                      <el-dropdown-item command="del">删除</el-dropdown-item>
                    </el-dropdown-menu>
                  </el-dropdown>
                </el-col>
              </el-row>
            </el-col>
          </el-row>
        </template>
      </el-tree>
    </div>

    <!-- 添加子部门对话框 -->
    <el-dialog :title="addDeptDialog.title" :visible.sync="addDeptDialog.visible">
      <el-form ref="refAddDeptForm" v-loading="addDeptDialog.isLoading" label-width="120px" :model="addDeptDialog.form.data" :rules="addDeptDialog.form.rules">
        <el-form-item label="部门名称" prop="name">
          <el-input ref="refDeptNameInputOfAdd" v-model="addDeptDialog.form.data.name" placeholder="2-10个字符" />
        </el-form-item>
        <el-form-item label="部门编码" prop="code">
          <el-input ref="refDeptCodeInputOfAdd" v-model="addDeptDialog.form.data.code" placeholder="2-10个字符" />
        </el-form-item>
        <el-form-item label="部门负责人" prop="managerId">
          <el-select v-model="addDeptDialog.form.data.managerId" style="width: 100%;" @visible-change="addDeptDialog.methods.onVisibleChangeOfManager">
            <el-option v-for="v of addDeptDialog.managerList" :key="v.id" :label="v.username" :value="v.id" />
          </el-select>
        </el-form-item>
        <el-form-item label="部门介绍" prop="introduce">
          <el-input v-model="addDeptDialog.form.data.introduce" type="textarea" placeholder="2-10个字符" />
        </el-form-item>
        <el-form-item>
          <el-button type="primary" @click="addDeptDialog.methods.ok">确定</el-button>
          <el-button @click="addDeptDialog.visible = false">取消</el-button>
        </el-form-item>
      </el-form>
    </el-dialog>

    <!-- 编辑部门对话框 -->
    <el-dialog :title="editDeptDialog.title" :visible.sync="editDeptDialog.visible">
      <el-form ref="refEditDeptForm" v-loading="editDeptDialog.isLoading" label-width="120px" :model="editDeptDialog.form.data" :rules="editDeptDialog.form.rules">
        <el-form-item label="部门名称" prop="name">
          <el-input ref="refDeptNameInputOfEdit" v-model="editDeptDialog.form.data.name" placeholder="2-10个字符" />
        </el-form-item>
        <el-form-item label="部门编码" prop="code">
          <el-input ref="refDeptCodeInputOfEdit" v-model="editDeptDialog.form.data.code" placeholder="2-10个字符" />
        </el-form-item>
        <el-form-item label="部门负责人" prop="managerId">
          <el-select v-model="editDeptDialog.form.data.managerId" style="width: 100%;" @visible-change="editDeptDialog.methods.onVisibleChangeOfManager">
            <el-option v-for="v of editDeptDialog.managerList" :key="v.id" :label="v.username" :value="v.id" />
          </el-select>
        </el-form-item>
        <el-form-item label="部门介绍" prop="introduce">
          <el-input v-model="editDeptDialog.form.data.introduce" type="textarea" placeholder="2-10个字符" />
        </el-form-item>
        <el-form-item>
          <el-button type="primary" @click="editDeptDialog.methods.ok">确定</el-button>
          <el-button @click="editDeptDialog.visible = false">取消</el-button>
        </el-form-item>
      </el-form>
    </el-dialog>
  </div>
</template>
<script>
import { getDepartments, getManagerList, addDepartment, editDepartment, getDepartment, delDepartment } from '@/api/department'
import _ from 'lodash'
export default {
  name: 'Department',
  data() {
    return {
      editDeptDialog: {
        title: '编辑子部门',
        managerList: [], // 部门负责人列表
        methods: Object.seal({
          // 打开对话框，department：部门对象
          open: async(department) => {
            // 打开之前，让列表开启加载中效果
            const loading = this.$loading({ target: this.$refs.refAppContainer })

            // 重置表单，虽然不需要重置数据，但是需要重置错误提示
            this.$refs.refEditDeptForm?.resetFields()
            // 在显示对话框之前，先加载部门负责人列表
            this.editDeptDialog.managerList = await getManagerList()

            // 发请求获取最新的部门详情，进行数据回显
            this.editDeptDialog.form.data = await getDepartment(department.id)

            // 优化对话框标题
            this.editDeptDialog.title = `编辑【${department.name}】`
            // 显示对话框
            this.editDeptDialog.visible = true

            // 关闭列表加载中效果
            loading.close()
          },
          // 部门负责人下拉列表显示隐藏时的回调
          onVisibleChangeOfManager: (visible) => {
            if (!visible) {
              this.$refs.refEditDeptForm.validateField('managerId')
            }
          },
          // 确定按钮回调
          ok: async() => {
            // 点击确定，立刻开启编辑表单加载中效果
            const loading = this.$loading({ target: this.$refs.refEditDeptForm.$el })

            try {
              // 验证整个表单
              await this.$refs.refEditDeptForm.validate()
              // 发送编辑请求
              await editDepartment(_.omit(this.editDeptDialog.form.data, ['managerName']))
            } finally {
              // finally 代码块 不管是否出现异常都会执行
              // 异常情况也要关闭编辑表单加载中效果：表单验证失败 or 更新请求失败
              loading.close()
            }
            // 成功消息提示
            this.$message.success('编辑成功')
            // 关闭弹层
            this.editDeptDialog.visible = false
            // 更新列表数据
            await this.getDepartments()
          }
        }),
        visible: false,
        form: {
          data: {
            id: '',
            name: '',
            code: '',
            managerId: '',
            introduce: '',
            pid: '', // 父部门的id
            createTime: ''
          },
          rules: Object.seal({
            name: [
              { required: true, message: '部门名称不能为空' },
              { min: 2, max: 10, message: '部门名称的长度为2-10个字符' },
              {
                validator: async(rule, value, cb) => {
                  // 开启部门名称输入框加载中效果
                  const loading = this.$loading({ target: this.$refs.refDeptNameInputOfEdit.$el })
                  const departments = await getDepartments()
                  // 关闭部门名称输入框加载中效果
                  loading.close()

                  // 注意：应该先排除 editDeptDialog.form.data.id 这一条对象
                  // 然后再看所有其他的部门中，有没有一个部门名称 等于 我当前准备 编辑的部门名称
                  if (departments.filter(v => v.id !== this.editDeptDialog.form.data.id).some(v => v.name === value)) {
                    cb(new Error('部门中已经有该名称了'))
                  } else {
                    cb()
                  }
                },
                // 当失焦时才验证这项
                trigger: 'blur'
              }
            ],
            code: [
              { required: true, message: '部门编码不能为空' },
              { min: 2, max: 10, message: '部门编码的长度为2-10个字符' },
              {
                validator: async(rule, value, cb) => {
                  // 开启部门编码输入框加载中效果
                  const loading = this.$loading({ target: this.$refs.refDeptCodeInputOfEdit.$el })
                  const departments = await getDepartments()
                  // 关闭部门编码输入框加载中效果
                  loading.close()

                  // 看所有部门中，有没有一个部门名称 等于 我当前准备 新增的部门名称
                  if (departments.filter(v => v.id !== this.editDeptDialog.form.data.id).some(v => v.code === value)) {
                    cb(new Error('部门中已经有该编码了'))
                  } else {
                    cb()
                  }
                },
                // 当失焦时才验证这项
                trigger: 'blur'
              }
            ],
            managerId: [{ required: true, message: '部门负责人不能为空' }],
            introduce: [
              { required: true, message: '部门介绍不能为空' },
              { min: 1, max: 100, message: '部门介绍的长度为1-100个字符' }
            ]
          })
        }
      },
      addDeptDialog: {
        title: '添加子部门',
        managerList: [], // 部门负责人列表
        methods: Object.seal({
          // 打开对话框，parentDepartment：父部门对象
          open: async(parentDepartment) => {
            // 开启列表加载中效果
            const loading = this.$loading({ target: this.$refs.refAppContainer })

            // 重置表单
            this.$refs.refAddDeptForm?.resetFields()
            // 在显示对话框之前，先加载部门负责人列表
            this.addDeptDialog.managerList = await getManagerList()
            // 记录父部门id
            this.addDeptDialog.form.data.pid = parentDepartment.id
            // 优化对话框标题
            this.addDeptDialog.title = `添加【${parentDepartment.name}】的子部门`
            // 显示对话框
            this.addDeptDialog.visible = true

            // 关闭列表加载中效果
            loading.close()
          },
          // 部门负责人下拉列表显示隐藏时的回调
          onVisibleChangeOfManager: (visible) => {
            if (!visible) {
              this.$refs.refAddDeptForm.validateField('managerId')
            }
          },
          // 确定按钮回调
          ok: async() => {
            // 开启添加表单加载中效果
            const loading = this.$loading({ target: this.$refs.refAddDeptForm.$el })
            try {
              // 验证整个表单
              await this.$refs.refAddDeptForm.validate()
              // 发送添加部门请求
              await addDepartment(this.addDeptDialog.form.data)
            } finally {
              // finally 代码块 不管是否出现异常都会执行
              // 异常情况也要关闭添加表单加载中效果：表单验证失败 or 添加请求失败
              loading.close()
            }
            // 成功消息提示
            this.$message.success('添加成功')
            // 关闭对话框
            this.addDeptDialog.visible = false
            // 更新列表数据
            await this.getDepartments()
          }
        }),
        visible: false,
        form: {
          data: {
            name: '',
            code: '',
            managerId: '',
            introduce: '',
            pid: '' // 父部门的id
          },
          rules: Object.seal({
            name: [
              { required: true, message: '部门名称不能为空' },
              { min: 2, max: 10, message: '部门名称的长度为2-10个字符' },
              {
                validator: async(rule, value, cb) => {
                  // 开启部门名称输入框加载中效果
                  const loading = this.$loading({ target: this.$refs.refDeptNameInputOfAdd.$el })
                  const departments = await getDepartments()
                  // 关闭部门名称输入框加载中效果
                  loading.close()

                  // 看所有部门中，有没有一个部门名称 等于 我当前准备 新增的部门名称
                  if (departments.some(v => v.name === value)) {
                    cb(new Error('部门中已经有该名称了'))
                  } else {
                    cb()
                  }
                },
                // 当失焦时才验证这项
                trigger: 'blur'
              }
            ],
            code: [
              { required: true, message: '部门编码不能为空' },
              { min: 2, max: 10, message: '部门编码的长度为2-10个字符' },
              {
                validator: async(rule, value, cb) => {
                  // 开启部门编号输入框加载中效果
                  const loading = this.$loading({ target: this.$refs.refDeptCodeInputOfAdd.$el })
                  const departments = await getDepartments()
                  // 关闭部门编号输入框加载中效果
                  loading.close()

                  // 看所有部门中，有没有一个部门名称 等于 我当前准备 新增的部门名称
                  if (departments.some(v => v.code === value)) {
                    cb(new Error('部门中已经有该编码了'))
                  } else {
                    cb()
                  }
                },
                // 当失焦时才验证这项
                trigger: 'blur'
              }
            ],
            managerId: [{ required: true, message: '部门负责人不能为空' }],
            introduce: [
              { required: true, message: '部门介绍不能为空' },
              { min: 1, max: 100, message: '部门介绍的长度为1-100个字符' }
            ]
          })
        }
      },
      list: [
        // {
        //   name: '传智教育',
        //   children: [
        //     { name: '总裁办' },
        //     { name: '行政部' },
        //     {
        //       name: '财务部',
        //       children: [
        //         { name: '财务部-1' },
        //         { name: '财务部-2' },
        //         { name: '财务部-3' }
        //       ]
        //     }
        //   ]
        // }
      ],
      treeProps: Object.seal({
        label: 'name', // 告诉 el-tree 组件 取数据源 节点对象中的 哪个属性 作为节点标题
        children: 'children' // 告诉 el-tree 组件 取数据源 节点对象中的 哪个属性 作为子节点
      })
    }
  },
  created() {
    this.getDepartments()
  },
  methods: {
    async handleCommand(command, data) {
      console.log(command, data)

      if (command === 'add') {
        // 添加子部门
        await this.addDeptDialog.methods.open(data)
      } else if (command === 'edit') {
        // 编辑部门
        await this.editDeptDialog.methods.open(data)
      } else if (command === 'del') {
        await this.$confirm(`你确定要删除"${data.name}"吗？`)

        // 开启列表加载中效果
        const loading = this.$loading({ target: this.$refs.refAppContainer })
        await delDepartment(data.id)
        // 关闭列表加载中效果
        loading.close()

        this.$message.success('删除成功')
        await this.getDepartments()
      }
    },
    async getDepartments() {
      // 发出请求前，开启加载中状态...
      const loading = this.$loading({ target: this.$refs.refAppContainer })

      const departments = await getDepartments()

      // 根据 pid 找儿子们
      function getChildren(pid) {
        return departments.filter(v => v.pid === pid)
      }

      // 循环每一个对象，都给他找一遍儿子
      departments.forEach(v => {
        // 找出所有 pid 等于 我的 id 的那一拨人，将找到的结果赋值给 children 属性
        v.children = getChildren(v.id)
      })

      // 将一级节点找出来，就是 pid === 0 的
      const list = departments.filter(v => v.pid === 0)
      console.log(list, 'list')

      this.list = list

      // 请求结束，并将数据处理完后，停止加载中状态...
      loading.close()
    }
  }
}
</script>

<style lang="scss" scoped>
.app-container {
  padding: 30px 140px;
  font-size: 14px;
}
</style>
